package com.xiaopeng.common.secure;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

/**
 * 加密解密工具类
 * 支持AES对称加密和RSA非对称加密
 */
public class CryptoUtils {

    private static final String AES_ALGORITHM = "AES";
    private static final String AES_CBC_PKCS5_PADDING = "AES/CBC/PKCS5Padding";
    private static final String RSA_ALGORITHM = "RSA";
    private static final String RSA_ECB_PKCS1_PADDING = "RSA/ECB/PKCS1Padding";
    private static final int AES_KEY_SIZE = 128;
    private static final int RSA_KEY_SIZE = 2048;
    private static final String SHA_256 = "SHA-256";
    private static final String MD5 = "MD5";

    // -------------------------- AES 对称加密 --------------------------

    /**
     * 生成AES密钥
     * @return Base64编码的密钥
     * @throws CryptoException 加密异常
     */
    public static String generateAESKey() throws CryptoException {
        try {
            KeyGenerator keyGenerator = KeyGenerator.getInstance(AES_ALGORITHM);
            keyGenerator.init(AES_KEY_SIZE);
            SecretKey secretKey = keyGenerator.generateKey();
            return Base64.getEncoder().encodeToString(secretKey.getEncoded());
        } catch (NoSuchAlgorithmException e) {
            throw new CryptoException("生成AES密钥失败", e);
        }
    }

    /**
     * AES加密
     * @param data 待加密数据
     * @param key Base64编码的密钥
     * @param iv Base64编码的初始化向量
     * @return Base64编码的加密结果
     * @throws CryptoException 加密异常
     */
    public static String aesEncrypt(String data, String key, String iv) throws CryptoException {
        try {
            byte[] keyBytes = Base64.getDecoder().decode(key);
            byte[] ivBytes = Base64.getDecoder().decode(iv);
            SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, AES_ALGORITHM);
            IvParameterSpec ivParameterSpec = new IvParameterSpec(ivBytes);

            Cipher cipher = Cipher.getInstance(AES_CBC_PKCS5_PADDING);
            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
            byte[] encrypted = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
            return Base64.getEncoder().encodeToString(encrypted);
        } catch (Exception e) {
            throw new CryptoException("AES加密失败", e);
        }
    }

    /**
     * AES解密
     * @param encryptedData Base64编码的加密数据
     * @param key Base64编码的密钥
     * @param iv Base64编码的初始化向量
     * @return 解密后的原始数据
     * @throws CryptoException 解密异常
     */
    public static String aesDecrypt(String encryptedData, String key, String iv) throws CryptoException {
        try {
            byte[] keyBytes = Base64.getDecoder().decode(key);
            byte[] ivBytes = Base64.getDecoder().decode(iv);
            byte[] encryptedBytes = Base64.getDecoder().decode(encryptedData);

            SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, AES_ALGORITHM);
            IvParameterSpec ivParameterSpec = new IvParameterSpec(ivBytes);

            Cipher cipher = Cipher.getInstance(AES_CBC_PKCS5_PADDING);
            cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
            byte[] decrypted = cipher.doFinal(encryptedBytes);
            return new String(decrypted, StandardCharsets.UTF_8);
        } catch (Exception e) {
            throw new CryptoException("AES解密失败", e);
        }
    }

    // -------------------------- RSA 非对称加密 --------------------------

    /**
     * 生成RSA密钥对
     * @return 包含公钥和私钥的KeyPair
     * @throws CryptoException 密钥生成异常
     */
    public static KeyPair generateRSAKeyPair() throws CryptoException {
        try {
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(RSA_ALGORITHM);
            keyPairGenerator.initialize(RSA_KEY_SIZE);
            return keyPairGenerator.generateKeyPair();
        } catch (NoSuchAlgorithmException e) {
            throw new CryptoException("生成RSA密钥对失败", e);
        }
    }

    /**
     * RSA公钥加密
     * @param data 待加密数据
     * @param publicKey Base64编码的公钥
     * @return Base64编码的加密结果
     * @throws CryptoException 加密异常
     */
    public static String rsaEncrypt(String data, String publicKey) throws CryptoException {
        try {
            byte[] keyBytes = Base64.getDecoder().decode(publicKey);
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
            KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
            PublicKey pubKey = keyFactory.generatePublic(keySpec);

            Cipher cipher = Cipher.getInstance(RSA_ECB_PKCS1_PADDING);
            cipher.init(Cipher.ENCRYPT_MODE, pubKey);
            byte[] encrypted = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
            return Base64.getEncoder().encodeToString(encrypted);
        } catch (Exception e) {
            throw new CryptoException("RSA加密失败", e);
        }
    }

    /**
     * RSA私钥解密
     * @param encryptedData Base64编码的加密数据
     * @param privateKey Base64编码的私钥
     * @return 解密后的原始数据
     * @throws CryptoException 解密异常
     */
    public static String rsaDecrypt(String encryptedData, String privateKey) throws CryptoException {
        try {
            byte[] keyBytes = Base64.getDecoder().decode(privateKey);
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
            KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
            PrivateKey priKey = keyFactory.generatePrivate(keySpec);

            Cipher cipher = Cipher.getInstance(RSA_ECB_PKCS1_PADDING);
            cipher.init(Cipher.DECRYPT_MODE, priKey);
            byte[] encryptedBytes = Base64.getDecoder().decode(encryptedData);
            byte[] decrypted = cipher.doFinal(encryptedBytes);
            return new String(decrypted, StandardCharsets.UTF_8);
        } catch (Exception e) {
            throw new CryptoException("RSA解密失败", e);
        }
    }

    // -------------------------- 签名验签 --------------------------

    /**
     * RSA私钥签名
     * @param data 待签名数据
     * @param privateKey Base64编码的私钥
     * @return Base64编码的签名
     * @throws CryptoException 签名异常
     */
    public static String rsaSign(String data, String privateKey) throws CryptoException {
        try {
            byte[] keyBytes = Base64.getDecoder().decode(privateKey);
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
            KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
            PrivateKey priKey = keyFactory.generatePrivate(keySpec);

            Signature signature = Signature.getInstance("SHA256withRSA");
            signature.initSign(priKey);
            signature.update(data.getBytes(StandardCharsets.UTF_8));
            byte[] signed = signature.sign();
            return Base64.getEncoder().encodeToString(signed);
        } catch (Exception e) {
            throw new CryptoException("RSA签名失败", e);
        }
    }

    /**
     * RSA公钥验签
     * @param data 原始数据
     * @param sign Base64编码的签名
     * @param publicKey Base64编码的公钥
     * @return 验签是否通过
     * @throws CryptoException 验签异常
     */
    public static boolean rsaVerify(String data, String sign, String publicKey) throws CryptoException {
        try {
            byte[] keyBytes = Base64.getDecoder().decode(publicKey);
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
            KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
            PublicKey pubKey = keyFactory.generatePublic(keySpec);

            Signature signature = Signature.getInstance("SHA256withRSA");
            signature.initVerify(pubKey);
            signature.update(data.getBytes(StandardCharsets.UTF_8));
            return signature.verify(Base64.getDecoder().decode(sign));
        } catch (Exception e) {
            throw new CryptoException("RSA验签失败", e);
        }
    }

    // -------------------------- 哈希算法 --------------------------

    /**
     * SHA256哈希
     * @param data 原始数据
     * @return 哈希值的十六进制字符串
     * @throws CryptoException 哈希计算异常
     */
    public static String sha256(String data) throws CryptoException {
        try {
            MessageDigest digest = MessageDigest.getInstance(SHA_256);
            byte[] hash = digest.digest(data.getBytes(StandardCharsets.UTF_8));
            return bytesToHex(hash);
        } catch (NoSuchAlgorithmException e) {
            throw new CryptoException("SHA256计算失败", e);
        }
    }

    /**
     * MD5哈希
     * @param data 原始数据
     * @return 哈希值的十六进制字符串
     * @throws CryptoException 哈希计算异常
     */
    public static String md5(String data) throws CryptoException {
        try {
            MessageDigest digest = MessageDigest.getInstance(MD5);
            byte[] hash = digest.digest(data.getBytes(StandardCharsets.UTF_8));
            return bytesToHex(hash);
        } catch (NoSuchAlgorithmException e) {
            throw new CryptoException("MD5计算失败", e);
        }
    }

    // -------------------------- 辅助方法 --------------------------

    private static String bytesToHex(byte[] bytes) {
        StringBuilder result = new StringBuilder();
        for (byte b : bytes) {
            result.append(String.format("%02x", b));
        }
        return result.toString();
    }

    /**
     * 自定义加密异常
     */
    public static class CryptoException extends Exception {
        public CryptoException(String message) {
            super(message);
        }

        public CryptoException(String message, Throwable cause) {
            super(message, cause);
        }
    }
}
